EC2上のPostgreSQLサーバーのダンプファイルをS3バケットへ保存・リストアしてみた

EC2上のPostgreSQLサーバーのダンプファイルをS3バケットへ保存・リストアしてみた

本ブログでは、PostgreSQLのネイティブバックアップツールであるpg_dumpを使ってS3にバックアップを保存し、リストアする方法を解説しています。
Clock Icon2024.12.04

どうもさいちゃんです。

皆さんは PostgreSQL のバックアップはどのようにとっていますか?
RDS の場合であれば自動バックアップ機能を使うという方も多いかもしれません。今回は PostgreSQL のネイティブバックアップとして提供されている pg_dump を利用し、S3 をバックアップの保存先に指定してみました。S3 に保存しているバックアップデータからリストアを行う方法とあわせてご紹介したいと思います。

今回の構成と前提条件

PostgreSQL のネイティブバックアップの保存先として S3 を指定する場合の前提条件は以下の通りです。

  • PostgreSQL16 がインストール/セットアップされている
  • AWS CLI がインストールされている
  • EC2 に S3 へアクセスするための適切な権限が付与されている
  • EC2 が S3 へアクセスするための経路が存在している

上記をふまえて今回は以下のような構成で検証を行いました。

postgresql_backup_1.png

やってみた

バックアップ保存先 S3 バケットの作成

まずはバックアップの保存先として指定する S3 を作成します。
今回は「postgresql-backup-test-<アカウント ID>」という名前で作成します。

postgresql_backup_2.png

設定値はすべてデフォルトで構いません。

EC2 用 IAM ロール作成

EC2 が S3 へアクセスするのに必要な権限を付与するロールを作成します。

まずはポリシーの作成からです。以下の様に適切な S3 バケットへの読み取り/書き込みアクセスを許可します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:ListAllMyBuckets",
      "Resource": "arn:aws:s3:::*"
    },
    {
      "Effect": "Allow",
      "Action": ["s3:ListBucket"],
      "Resource": ["arn:aws:s3:::バケット名"]
    },
    {
      "Effect": "Allow",
      "Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
      "Resource": ["arn:aws:s3:::バケット名/*"]
    }
  ]
}

適当な名前を付けてポリシーを保存します。

postgresql_backup_3.png

ロールを作成します。
信頼されたエンティティでユースケースから EC2 を選択して、許可ポリシーで先ほど作成した IAM ポリシーを選択します。

postgresql_backup_4.png

postgresql_backup_5.png

ロールを作成したら該当の EC2 へロールをアタッチしてください。

テスト用 DB の作成

PostgreSQL16 をインストール/セットアップした EC2 でテスト用の DB の作成を行っていきます。
EC2 への PostgreSQL のインストールやセットアップ方法に関しては本ブログの趣旨と少しずれるので割愛します。

まずは PostgreSQL16 に接続した状態で DB 作成を行います。

# DB作成
postgres=# CREATE DATABASE backupdb;
CREATE DATABASE
# DB一覧を確認
postgres=# \l
                                                       List of databases
   Name    |  Owner   | Encoding | Locale Provider |   Collate   |    Ctype    | ICU Locale | ICU Rules |   Access privileges
-----------+----------+----------+-----------------+-------------+-------------+------------+-----------+-----------------------
 backupdb  | postgres | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |            |           |
 postgres  | postgres | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |            |           |
 template0 | postgres | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |            |           | =c/postgres          +
           |          |          |                 |             |             |            |           | postgres=CTc/postgres
 template1 | postgres | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |            |           | =c/postgres          +
           |          |          |                 |             |             |            |           | postgres=CTc/postgres
 test      | postgres | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |            |           |
(5 rows)


「backupdb」という名前の DB が新しく作成されました。
続けて、データを格納するテーブルの作成も行っていきます。

# テーブル作成
backupdb=# CREATE TABLE test_table1
(id CHAR(4) NOT NULL,
name TEXT NOT NULL,
fruit TEXT ,
PRIMARY KEY (id));
CREATE TABLE

backupdb=# CREATE TABLE test_table2
(id CHAR(4) NOT NULL,
name TEXT NOT NULL,
age INTEGER ,
PRIMARY KEY (id));
CREATE TABLE

今回は、idnamefruitという3つのカラムが含まれる test_table1 と、idnameageの3つのカラムが含まれる test_table2 を作成しました。\dで DB 内のテーブルを確認してみます。

# テーブル一覧を確認
backupdb=# \d
List of relations
Schema | Name | Type | Owner
--------+-------------+-------+----------
public | test_table1 | table | postgres
public | test_table2 | table | postgres
(2 rows)

テーブルが2つ出来ています。
では、このテーブルに実際にデータを入れていきます。

# データを格納
backupdb=# INSERT INTO test_table1(id,name,fruit) VALUES (1,'aoki','banana');

backupdb=# INSERT INTO test_table2(id,name,age) VALUES (1,'takahashi','28');

上記の要領で複数データを入れました。
seclet * from テーブル名;で入れたデータを確認してみます。

# テーブル内のデータの確認
backupdb=# select * from test_table1;
id | name | fruit
------+--------+--------
1 | aoki | banana
2 | yamada | apple
(2 rows)

backupdb=# select * from test_table2;
id | name | age
------+-----------+-----
1 | takahashi | 28
2 | sato | 45
(2 rows)

問題なくデータが格納されていることが確認できました。

バックアップの取得

では実際にバックアップを取得します。

$ pg_dump -h localhost -d backupdb -U postgres  -v -Fc  | aws s3 cp - s3://バケット名/test.dump

上記のコマンドでダンプファイルをさきほど作成した S3 バケットへtest.dumpという名前で保存します。

コマンドを実行すると-U で指定したユーザーのパスワード入力を求められるので、入力します。
以下が実行結果です。

$ pg_dump -h localhost -d backupdb -U postgres  -v -Fc | aws s3 cp - s3://postgresql-backup-test-454415427829/test.dump
Password: パスワードを入力
pg_dump: last built-in OID is 16383
pg_dump: reading extensions
pg_dump: identifying extension members
pg_dump: reading schemas
pg_dump: reading user-defined tables
pg_dump: reading user-defined functions
pg_dump: reading user-defined types
pg_dump: reading procedural languages
pg_dump: reading user-defined aggregate functions
pg_dump: reading user-defined operators
...以下省略

バックアップが取れているのかコンソールから S3 を確認してみます。

postgresql_backup_6.png

問題なくバックアップが取れています。

リストア

初めに、特定のテーブルのみをリストアをしてみます。
test_table1 を削除してみます。

# テーブルを削除
backupdb=# DROP TABLE test_table1;
DROP TABLE
# テーブル一覧を確認
backupdb=# \d
            List of relations
 Schema |    Name     | Type  |  Owner
--------+-------------+-------+----------
 public | test_table2 | table | postgres
(1 row)

テーブルが削除されていることが確認できたので、リストアしてみましょう。


$ aws s3 cp s3://バケット名/test.dump - | pg_restore -h localhost -d backupdb -U postgres  -t test_table1
Password:パスワードを入力

-t で特定のテーブルのみを復元出来ます。

エラー無くリストアが完了したのでテーブルを確認してみます。

# テーブルの一覧を確認
backupdb=# \d
            List of relations
 Schema |    Name     | Type  |  Owner
--------+-------------+-------+----------
 public | test_table1 | table | postgres
 public | test_table2 | table | postgres
(2 rows)

# test_table1内のデータを確認
backupdb=# select * from test_table1;
id | name | fruit
------+--------+--------
1 | aoki | banana
2 | yamada | apple
(2 rows)


test_table1 が復元されています。
格納されていたデータも元に戻っています。

今度は DB 全体のリストアを試すために、backupdb を削除してみます。

# バックアップ用DB削除
postgres=# DROP DATABASE backupdb;
DROP DATABASE
postgres=# \l
                                                       List of databases
   Name    |  Owner   | Encoding | Locale Provider |   Collate   |    Ctype    | ICU Locale | ICU Rules |   Access privileges
-----------+----------+----------+-----------------+-------------+-------------+------------+-----------+-----------------------
 postgres  | postgres | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |            |           |
 template0 | postgres | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |            |           | =c/postgres          +
           |          |          |                 |             |             |            |           | postgres=CTc/postgres
 template1 | postgres | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |            |           | =c/postgres          +
           |          |          |                 |             |             |            |           | postgres=CTc/postgres
 test      | postgres | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |            |           |
(4 rows)

# リストア用DB作成
postgres=# CREATE DATABASE restoredb;
CREATE DATABASE
postgres=# \l
                                                       List of databases
   Name    |  Owner   | Encoding | Locale Provider |   Collate   |    Ctype    | ICU Locale | ICU Rules |   Access privileges
-----------+----------+----------+-----------------+-------------+-------------+------------+-----------+-----------------------
 postgres  | postgres | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |            |           |
 restoredb | postgres | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |            |           |
 template0 | postgres | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |            |           | =c/postgres          +
           |          |          |                 |             |             |            |           | postgres=CTc/postgres
 template1 | postgres | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |            |           | =c/postgres          +
           |          |          |                 |             |             |            |           | postgres=CTc/postgres
 test      | postgres | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |            |           |
(5 rows)

backupdb が消えました。同時にリストア用に新たに restoredb を作成しています。
今回はこの restoredb にダンプファイルからリストアしてみます。

$ aws s3 cp s3://バケット名/test.dump - | pg_restore -h localhost -d restoredb -U postgres
Password: パスワードを入力

エラー無くリストアが終了したので、restoredb の中身を見てみましょう。

# restordb内のテーブル一覧を確認
restoredb=# \d
            List of relations
 Schema |    Name     | Type  |  Owner
--------+-------------+-------+----------
 public | test_table1 | table | postgres
 public | test_table2 | table | postgres
(2 rows)

# それぞれのテーブル内のデータを確認
restoredb=# select * from test_table1;
  id  |  name  | fruit
------+--------+--------
 1    | aoki   | banana
 2    | yamada | apple
(2 rows)

restoredb=# select * from test_table2;
  id  |   name    | age
------+-----------+-----
 1    | takahashi |  28
 2    | sato      |  45
(2 rows)

先ほど backupdb と同じデータが restoredb に復元されています。

最後に

オンプレミス環境から AWS への移行において、様々な要件により RDS ではなく EC2 上にデータベースを構築するケースがあります。
その際、バックアップデータを EC2 インスタンスとは別に S3 に保存することで、万が一の障害時にもデータを確実に復旧できる、より安全なバックアップ体制を構築できます。AWS 上で PostgreSQL のネイティブバックアップを使用したい場合は本ブログで紹介した方法を検討してみても良いかもしれません。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.